//*****************************************************************************
// JFab (http://code.google.com/p/jfab)
// Copyright (c) 2011 JFab.org
// Admin jfab@jeffreythompson.net
//
// See the file "LICENSE.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//*****************************************************************************

package org.jfab.core.inflator;

import org.jfab.core.analyzer.InterfaceAnalyzer;
import org.jfab.core.builder.BuilderGroup;
import org.jfab.core.builder.CGClassBuilder;
import org.jfab.core.collection.CGClassList;
import org.jfab.core.collection.CGEnumList;
import org.jfab.core.collection.CGInterfaceList;
import org.jfab.core.model.CGClass;
import org.jfab.core.model.CGEnum;
import org.jfab.core.model.CGInterface;
import org.jfab.core.model.CGSpec;
import org.jfab.core.model.CGSpecUtilities;
import org.jfab.core.model.CGTypeUtilities;
import org.jfab.core.model.Converter;
import org.jfab.core.model.JUnitTest;
import org.jfab.core.model.SimulatedData;

/**
 * Provides a default implementation of a code generator specification inflator.
 */
public class DefaultCGSpecInflator implements CGSpecInflator
{
    /** Builder group. */
    private final BuilderGroup _builderGroup;

    /** Inflator group. */
    private final InflatorGroup _inflatorGroup;

    /** Interface analyzer. */
    private final InterfaceAnalyzer _interfaceAnalyzer;

    /** Code generator type utilities. */
    private final CGTypeUtilities _cgTypeUtils;

    /**
     * Construct this object with the given parameters.
     * 
     * @param cgTypeUtils Code generator type utilities. (required)
     * @param builderGroup Builder group.
     * @param inflatorGroup Inflator group.
     * @param interfaceAnalyzer Interface analyzer.
     */
    public DefaultCGSpecInflator(final CGTypeUtilities cgTypeUtils,
            final BuilderGroup builderGroup, final InflatorGroup inflatorGroup,
            final InterfaceAnalyzer interfaceAnalyzer)
    {
        _cgTypeUtils = cgTypeUtils;
        _builderGroup = builderGroup;
        _inflatorGroup = inflatorGroup;
        _interfaceAnalyzer = interfaceAnalyzer;
    }

    @Override
    public CGSpec inflate(final CGSpec source)
    {
        final CGSpec answer = new CGSpec();

        answer.getInterfaces().addAll(inflateInterfaces(source));
        answer.getClasses().addAll(inflateClasses(source));
        answer.getEnums().addAll(inflateEnums(source));

        return answer;
    }

    /**
     * @param cgConverter Code generator converter.
     * 
     * @return a new class created from the given parameter.
     */
    private CGClass createClass(final Converter cgConverter)
    {
        return getConverterClassBuilder().build(cgConverter);
    }

    /**
     * @param cgJUnitTest Code generator JUnit test.
     * 
     * @return a new class created from the given parameter.
     */
    private CGClass createClass(final JUnitTest cgJUnitTest)
    {
        return getJUnitTestClassBuilder().build(cgJUnitTest);
    }

    /**
     * @param cgSimulatedData Code generator simulated data.
     * 
     * @return a new class created from the given parameter.
     */
    private CGClass createClass(final SimulatedData cgSimulatedData)
    {
        return getSimulatedDataClassBuilder().build(cgSimulatedData);
    }

    /**
     * @return the builderGroup
     */
    private BuilderGroup getBuilderGroup()
    {
        return _builderGroup;
    }

    /**
     * @return classInflator
     */
    private CGClassInflator getClassInflator()
    {
        return getInflatorGroup().getClassInflator();
    }

    /**
     * @return the converterClassBuilder
     */
    private CGClassBuilder<Converter> getConverterClassBuilder()
    {
        return getBuilderGroup().getConverterClassBuilder();
    }

    /**
     * @return enumInflator
     */
    private CGEnumInflator getEnumInflator()
    {
        return getInflatorGroup().getEnumInflator();
    }

    /**
     * @return the inflatorGroup
     */
    private InflatorGroup getInflatorGroup()
    {
        return _inflatorGroup;
    }

    /**
     * @return interfaceInflator
     */
    private CGInterfaceInflator getInterfaceInflator()
    {
        return getInflatorGroup().getInterfaceInflator();
    }

    /**
     * @return the jUnitTestClassBuilder
     */
    private CGClassBuilder<JUnitTest> getJUnitTestClassBuilder()
    {
        return getBuilderGroup().getJUnitTestClassBuilder();
    }

    /**
     * @return the simulatedDataClassBuilder
     */
    private CGClassBuilder<SimulatedData> getSimulatedDataClassBuilder()
    {
        return getBuilderGroup().getSimulatedDataClassBuilder();
    }

    /**
     * @param inflatedSpec Inflated code generator specification.
     * @param cgClass Code generator class.
     * 
     * @return a new class created from the given parameter.
     */
    private CGClass inflateClass(final CGSpec inflatedSpec,
            final CGClass cgClass)
    {
        final CGSpecUtilities specUtils = new CGSpecUtilities(inflatedSpec,
                _cgTypeUtils);
        final CGInterfaceList superInterfaces = specUtils.getSuperInterfaces(
                cgClass, _interfaceAnalyzer);

        return getClassInflator().inflate(cgClass, superInterfaces);
    }

    /**
     * @param source Source code generator root.
     * 
     * @return the given parameter inflated into a list of code generator
     *         classes.
     */
    private CGClassList inflateClasses(final CGSpec source)
    {
        final CGClassList answer = new CGClassList();

        for (final CGClass cgClass : source.getClasses())
        {
            final CGClass inflatedClass = inflateClass(source, cgClass);
            answer.add(inflatedClass);
        }

        for (final Converter converter : source.getConverters())
        {
            final CGClass cgClass = createClass(converter);
            answer.add(cgClass);
        }

        for (final JUnitTest jUnitTest : source.getJUnitTests())
        {
            final CGClass cgClass = createClass(jUnitTest);
            answer.add(cgClass);
        }

        for (final SimulatedData simulatedData : source.getSimulatedData())
        {
            final CGClass cgClass = createClass(simulatedData);
            answer.add(cgClass);
        }

        return answer;
    }

    /**
     * @param cgEnum Code generator enum.
     * 
     * @return a new enum created from the given parameter.
     */
    private CGEnum inflateEnum(final CGEnum cgEnum)
    {
        final CGInterfaceList superInterfaces = null;

        return getEnumInflator().inflate(cgEnum, superInterfaces);
    }

    /**
     * @param source Source code generator root.
     * 
     * @return the given parameter inflated into a list of code generator
     *         classes.
     */
    private CGEnumList inflateEnums(final CGSpec source)
    {
        final CGEnumList answer = new CGEnumList();

        for (final CGEnum cgEnum : source.getEnums())
        {
            final CGEnum inflatedEnum = inflateEnum(cgEnum);
            answer.add(inflatedEnum);
        }

        return answer;
    }

    /**
     * @param inflatedSpec Inflated code generator specification.
     * @param cgInterface Code generator interface.
     * 
     * @return a new interface created from the given parameter.
     */
    private CGInterface inflateInterface(final CGSpec inflatedSpec,
            final CGInterface cgInterface)
    {
        final CGSpecUtilities specUtils = new CGSpecUtilities(inflatedSpec,
                _cgTypeUtils);
        final CGInterfaceList superInterfaces = specUtils.getSuperInterfaces(
                cgInterface, _interfaceAnalyzer);

        return getInterfaceInflator().inflate(cgInterface, superInterfaces);
    }

    /**
     * @param source Source code generator root.
     * 
     * @return the given parameter inflated into a list of code generator
     *         classes.
     */
    private CGInterfaceList inflateInterfaces(final CGSpec source)
    {
        final CGInterfaceList answer = new CGInterfaceList();

        for (final CGInterface cgInterface : source.getInterfaces())
        {
            final CGInterface inflatedInterface = inflateInterface(source,
                    cgInterface);
            answer.add(inflatedInterface);
        }

        return answer;
    }
}
